package edu.cuny.baruch.cis9590.library.gui;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import java.util.Date;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import edu.cuny.baruch.cis9590.library.database.BookDAO;
import edu.cuny.baruch.cis9590.library.database.BorrowDAO;
import edu.cuny.baruch.cis9590.library.database.PatronDAO;
import edu.cuny.baruch.cis9590.library.entities.Book;
import edu.cuny.baruch.cis9590.library.entities.Borrow;
import edu.cuny.baruch.cis9590.library.entities.Patron;

final class PatronSearchPanel extends LibraryPanel {
    
    private static final NumberFormat CURRENCY_FORMATTER = NumberFormat.getCurrencyInstance();
    private static final long MILLIS_IN_DAY = 1000 * 60 * 60 * 24;
    
    private static final float FINE_PER_DAY = 0.50f;
    
    private final BookDAO bookDAO;
    private final BorrowDAO borrowDAO;
    private final JTextField finesField;
    private final JTextField nameField;
    private final BookTable patronBookTable;
    private final PatronDAO patronDAO;
    private final JButton payFinesButton;
    private final JButton returnButton;
    private final JTextField searchField;
    
    private Patron currentPatron;
    
    public PatronSearchPanel() {
        this.bookDAO = new BookDAO();
        this.borrowDAO = new BorrowDAO();
        this.patronDAO = new PatronDAO();
        
        GridBagLayout gridBagLayout = new GridBagLayout();
        gridBagLayout.columnWidths = new int[]{0, 0, 0, 0, 0, 0, 0, 0};
        gridBagLayout.rowHeights = new int[]{0, 0, 0, 0, 0, 0, 0};
        gridBagLayout.columnWeights = new double[]{0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, Double.MIN_VALUE};
        gridBagLayout.rowWeights = new double[]{0.0, 0.0, 0.0, 1.0, 0.0, 0.0, Double.MIN_VALUE};
        setLayout(gridBagLayout);
        
        JLabel idLabel = new JLabel("ID:");
        GridBagConstraints gbc_idLabel = new GridBagConstraints();
        gbc_idLabel.insets = new Insets(0, 0, 5, 5);
        gbc_idLabel.anchor = GridBagConstraints.EAST;
        gbc_idLabel.gridx = 1;
        gbc_idLabel.gridy = 1;
        add(idLabel, gbc_idLabel);
        
        searchField = new JTextField();
        idLabel.setLabelFor(searchField);
        GridBagConstraints gbc_patronSearchField = new GridBagConstraints();
        gbc_patronSearchField.gridwidth = 3;
        gbc_patronSearchField.insets = new Insets(0, 0, 5, 5);
        gbc_patronSearchField.fill = GridBagConstraints.HORIZONTAL;
        gbc_patronSearchField.gridx = 2;
        gbc_patronSearchField.gridy = 1;
        add(searchField, gbc_patronSearchField);
        
        JButton searchButton = new JButton("Search");
        searchButton.addActionListener(new PatronSearchActionListener());
        GridBagConstraints gbc_searchButton = new GridBagConstraints();
        gbc_searchButton.insets = new Insets(0, 0, 5, 5);
        gbc_searchButton.gridx = 5;
        gbc_searchButton.gridy = 1;
        add(searchButton, gbc_searchButton);
        
        JLabel nameLabel = new JLabel("Name:");
        GridBagConstraints gbc_nameLabel = new GridBagConstraints();
        gbc_nameLabel.anchor = GridBagConstraints.EAST;
        gbc_nameLabel.insets = new Insets(0, 0, 5, 5);
        gbc_nameLabel.gridx = 1;
        gbc_nameLabel.gridy = 2;
        add(nameLabel, gbc_nameLabel);
        
        nameField = new JTextField();
        nameLabel.setLabelFor(nameField);
        nameField.setEditable(false);
        GridBagConstraints gbc_nameField = new GridBagConstraints();
        gbc_nameField.insets = new Insets(0, 0, 5, 5);
        gbc_nameField.fill = GridBagConstraints.HORIZONTAL;
        gbc_nameField.gridx = 2;
        gbc_nameField.gridy = 2;
        add(nameField, gbc_nameField);
        
        JLabel finesLabel = new JLabel("Fines:");
        GridBagConstraints gbc_finesLabel = new GridBagConstraints();
        gbc_finesLabel.anchor = GridBagConstraints.EAST;
        gbc_finesLabel.insets = new Insets(0, 0, 5, 5);
        gbc_finesLabel.gridx = 3;
        gbc_finesLabel.gridy = 2;
        add(finesLabel, gbc_finesLabel);
        
        finesField = new JTextField();
        finesLabel.setLabelFor(finesField);
        finesField.setEditable(false);
        GridBagConstraints gbc_finesField = new GridBagConstraints();
        gbc_finesField.insets = new Insets(0, 0, 5, 5);
        gbc_finesField.fill = GridBagConstraints.HORIZONTAL;
        gbc_finesField.gridx = 4;
        gbc_finesField.gridy = 2;
        add(finesField, gbc_finesField);
        
        JScrollPane scrollPane = new JScrollPane();
        GridBagConstraints gbc_scrollPane = new GridBagConstraints();
        gbc_scrollPane.gridwidth = 5;
        gbc_scrollPane.insets = new Insets(0, 0, 5, 5);
        gbc_scrollPane.fill = GridBagConstraints.BOTH;
        gbc_scrollPane.gridx = 1;
        gbc_scrollPane.gridy = 3;
        add(scrollPane, gbc_scrollPane);
        
        patronBookTable = new BookTable();
        patronBookTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        patronBookTable.getSelectionModel().addListSelectionListener(new BookSearchSelectionListener());
        patronBookTable.setFillsViewportHeight(true);
        scrollPane.setViewportView(patronBookTable);
        
        returnButton = new JButton("Return");
        returnButton.addActionListener(new ReturnActionListener());
        returnButton.setEnabled(false);
        GridBagConstraints gbc_btnReturn = new GridBagConstraints();
        gbc_btnReturn.insets = new Insets(0, 0, 5, 5);
        gbc_btnReturn.gridx = 2;
        gbc_btnReturn.gridy = 4;
        add(returnButton, gbc_btnReturn);
        
        payFinesButton = new JButton("Pay Fines");
        payFinesButton.addActionListener(new PayFinesActionListener());
        GridBagConstraints gbc_btnPayFines = new GridBagConstraints();
        gbc_btnPayFines.insets = new Insets(0, 0, 5, 5);
        gbc_btnPayFines.gridx = 4;
        gbc_btnPayFines.gridy = 4;
        add(payFinesButton, gbc_btnPayFines);
    }
    
    private void updatePatronInfo() {
        nameField.setText(currentPatron.getName());
        finesField.setText(CURRENCY_FORMATTER.format(currentPatron.getFines()));
        patronBookTable.setBooks(bookDAO.getByPatron(currentPatron.getId()));
        payFinesButton.setEnabled(currentPatron != null && currentPatron.getFines() > 0.0f);
    }
    
    private final class PatronSearchActionListener implements ActionListener {
        @Override public void actionPerformed(ActionEvent e) {
            int id = -1;
            try {
                id = Integer.parseInt(searchField.getText());
            } catch (NumberFormatException nfe){
                JOptionPane.showMessageDialog(PatronSearchPanel.this, "Invalid user ID");
                searchField.setText("");
                return;
            }
            
            currentPatron = patronDAO.getById(id);
            if (currentPatron == null) {
                JOptionPane.showMessageDialog(PatronSearchPanel.this, "Invalid user ID");
                searchField.setText("");
                return;
            }
            
            updatePatronInfo();
        }
    }
    
    private final class ReturnActionListener implements ActionListener {
        @Override public void actionPerformed(ActionEvent e) {
            Book book = patronBookTable.getSelectedBook();
            Borrow borrow = borrowDAO.getByBookId(book.getId());
            
            book.setStatus('A');
            long daysLate = (new Date().getTime() - borrow.getDueDate().getTime()) / MILLIS_IN_DAY;
            if (daysLate > 0) {
                currentPatron.setFines(daysLate * FINE_PER_DAY + currentPatron.getFines());
            }
            
            bookDAO.update(book);
            borrowDAO.delete(borrow);
            patronDAO.update(currentPatron);
        }
    }
    
    private final class PayFinesActionListener implements ActionListener {
        @Override public void actionPerformed(ActionEvent e) {
            float finesPaid;
            try {
                String input = JOptionPane.showInputDialog(PatronSearchPanel.this, "Enter amount paid:");
                finesPaid = Float.parseFloat(input);
                if (finesPaid <= 0.0f || finesPaid > currentPatron.getFines()) {
                    throw new NumberFormatException();
                }
            } catch (NumberFormatException nfe) {
                JOptionPane.showMessageDialog(PatronSearchPanel.this, "Invalid amount.");
                return;
            }
            currentPatron.setFines(currentPatron.getFines() - finesPaid);
            patronDAO.update(currentPatron);
        }
    }
    
    private final class BookSearchSelectionListener implements ListSelectionListener {
        @Override public void valueChanged(ListSelectionEvent e) {
            Book selectedBook = patronBookTable.getSelectedBook();
            returnButton.setEnabled(selectedBook != null);
        }
    }
    
    @Override
    public void clear() {
        finesField.setText("");
        nameField.setText("");
        patronBookTable.clear();
    }
    
}
